// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/font_warmup_win.h"

#include <stddef.h>
#include <stdint.h>
#include <windows.h>

#include <memory>
#include <vector>

#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "base/win/windows_version.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkString.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "third_party/skia/include/ports/SkFontMgr.h"

namespace content {

namespace {

    class TestSkTypeface : public SkTypeface {
    public:
        TestSkTypeface(const SkFontStyle& style,
            const char* familyName,
            SkFontTableTag tag,
            const char* data,
            size_t dataLength)
            : SkTypeface(style, 0)
            , familyName_(familyName)
            , tag_(tag)
            , data_(data, data + dataLength)
        {
        }

    protected:
        SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
            const SkDescriptor*) const override
        {
            ADD_FAILURE();
            return nullptr;
        }
        void onFilterRec(SkScalerContextRec*) const override { ADD_FAILURE(); }
        SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
            PerGlyphInfo,
            const uint32_t* glyphIDs,
            uint32_t glyphIDsCount) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkStreamAsset* onOpenStream(int* ttcIndex) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override
        {
            ADD_FAILURE();
        }

        int onCharsToGlyphs(const void* chars,
            Encoding,
            uint16_t glyphs[],
            int glyphCount) const override
        {
            ADD_FAILURE();
            return 0;
        }

        int onCountGlyphs() const override
        {
            ADD_FAILURE();
            return 0;
        }

        int onGetUPEM() const override
        {
            ADD_FAILURE();
            return 0;
        }
        bool onGetKerningPairAdjustments(const uint16_t glyphs[],
            int count,
            int32_t adjustments[]) const override
        {
            ADD_FAILURE();
            return false;
        }

        void onGetFamilyName(SkString* familyName) const override
        {
            *familyName = familyName_;
        }

        LocalizedStrings* onCreateFamilyNameIterator() const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        int onGetTableTags(SkFontTableTag tags[]) const override
        {
            ADD_FAILURE();
            return 0;
        }

        size_t onGetTableData(SkFontTableTag tag,
            size_t offset,
            size_t length,
            void* data) const override
        {
            size_t retsize = 0;
            if (tag == tag_) {
                retsize = length > data_.size() ? data_.size() : length;
                if (data)
                    memcpy(data, &data_[0], retsize);
            }
            return retsize;
        }

        bool onComputeBounds(SkRect*) const override
        {
            ADD_FAILURE();
            return false;
        }

    private:
        SkString familyName_;
        SkFontTableTag tag_;
        std::vector<char> data_;
    };

    const char* kTestFontFamily = "GDITest";
    const wchar_t* kTestFontFamilyW = L"GDITest";
    const SkFontTableTag kTestFontTableTag = 0x11223344;
    const char* kTestFontData = "GDITestGDITest";
    const wchar_t* kTestFontFamilyInvalid = L"InvalidFont";

    class TestSkFontMgr : public SkFontMgr {
    public:
        TestSkFontMgr()
        {
            content::SetPreSandboxWarmupFontMgrForTesting(sk_ref_sp(this));
        }
        ~TestSkFontMgr() override
        {
            content::SetPreSandboxWarmupFontMgrForTesting(nullptr);
        }

    protected:
        int onCountFamilies() const override { return 1; }

        void onGetFamilyName(int index, SkString* familyName) const override
        {
            if (index == 0)
                *familyName = kTestFontFamily;
        }

        SkFontStyleSet* onCreateStyleSet(int index) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkFontStyleSet* onMatchFamily(const char familyName[]) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkTypeface* onMatchFamilyStyle(const char familyName[],
            const SkFontStyle&) const override
        {
            if (strcmp(familyName, kTestFontFamily) == 0)
                return createTypeface();
            return nullptr;
        }

        SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
            const SkFontStyle&,
            const char* bcp47[],
            int bcp47Count,
            SkUnichar character) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkTypeface* onMatchFaceStyle(const SkTypeface*,
            const SkFontStyle&) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkTypeface* onCreateFromData(SkData*, int ttcIndex) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkTypeface* onCreateFromStream(SkStreamAsset*, int ttcIndex) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

        SkTypeface* onLegacyCreateTypeface(const char familyName[],
            SkFontStyle style) const override
        {
            ADD_FAILURE();
            return nullptr;
        }

    private:
        SkTypeface* createTypeface() const
        {
            SkFontStyle style(400, 100, SkFontStyle::kUpright_Slant);

            return new TestSkTypeface(style, kTestFontFamily,
                base::ByteSwap(kTestFontTableTag), kTestFontData,
                strlen(kTestFontData));
        }
    };

    void InitLogFont(LOGFONTW* logfont, const wchar_t* fontname)
    {
        size_t length = std::min(sizeof(logfont->lfFaceName),
            (wcslen(fontname) + 1) * sizeof(wchar_t));
        memcpy(logfont->lfFaceName, fontname, length);
    }

    content::GdiFontPatchData* SetupTest()
    {
        HMODULE module_handle;
        if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                reinterpret_cast<LPCWSTR>(SetupTest),
                &module_handle)) {
            WCHAR module_path[MAX_PATH];

            if (GetModuleFileNameW(module_handle, module_path, MAX_PATH) > 0) {
                base::FilePath path(module_path);
                content::ResetEmulatedGdiHandlesForTesting();
                return content::PatchGdiFontEnumeration(path);
            }
        }
        return nullptr;
    }

    int CALLBACK EnumFontCallbackTest(const LOGFONT* log_font,
        const TEXTMETRIC* text_metric,
        DWORD font_type,
        LPARAM param)
    {
        const NEWTEXTMETRICEX* new_text_metric = reinterpret_cast<const NEWTEXTMETRICEX*>(text_metric);

        return !(font_type & TRUETYPE_FONTTYPE) && !(new_text_metric->ntmTm.ntmFlags & NTM_PS_OPENTYPE);
    }

} // namespace

TEST(GDIFontEmulationTest, CreateDeleteDCSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_FALSE(!patch_data);

    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    EXPECT_EQ(1u, GetEmulatedGdiHandleCountForTesting());
    EXPECT_TRUE(DeleteDC(hdc));
    EXPECT_EQ(0u, GetEmulatedGdiHandleCountForTesting());
}

TEST(GDIFontEmulationTest, CreateUniqueDCSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);

    HDC hdc1 = CreateCompatibleDC(0);
    EXPECT_NE(hdc1, nullptr);
    HDC hdc2 = CreateCompatibleDC(0);
    EXPECT_NE(hdc2, nullptr);
    EXPECT_NE(hdc1, hdc2);
    EXPECT_TRUE(DeleteDC(hdc2));
    EXPECT_EQ(1u, GetEmulatedGdiHandleCountForTesting());
    EXPECT_TRUE(DeleteDC(hdc1));
    EXPECT_EQ(0u, GetEmulatedGdiHandleCountForTesting());
}

TEST(GDIFontEmulationTest, CreateFontSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyW);
    HFONT font = CreateFontIndirectW(&logfont);
    EXPECT_NE(font, nullptr);
    EXPECT_TRUE(DeleteObject(font));
    EXPECT_EQ(0u, GetEmulatedGdiHandleCountForTesting());
}

TEST(GDIFontEmulationTest, CreateFontFailure)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyInvalid);
    HFONT font = CreateFontIndirectW(&logfont);
    EXPECT_EQ(font, nullptr);
}

TEST(GDIFontEmulationTest, EnumFontFamilySuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyW);
    int res = EnumFontFamiliesExW(hdc, &logfont, EnumFontCallbackTest, 0, 0);
    EXPECT_FALSE(res);
    EXPECT_TRUE(DeleteDC(hdc));
}

TEST(GDIFontEmulationTest, EnumFontFamilyFailure)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyInvalid);
    int res = EnumFontFamiliesExW(hdc, &logfont, EnumFontCallbackTest, 0, 0);
    EXPECT_TRUE(res);
    EXPECT_TRUE(DeleteDC(hdc));
}

TEST(GDIFontEmulationTest, DeleteDCFailure)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = reinterpret_cast<HDC>(0x55667788);
    EXPECT_FALSE(DeleteDC(hdc));
}

TEST(GDIFontEmulationTest, DeleteObjectFailure)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HFONT font = reinterpret_cast<HFONT>(0x88aabbcc);
    EXPECT_FALSE(DeleteObject(font));
}

TEST(GDIFontEmulationTest, GetFontDataSizeSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyW);
    HFONT font = CreateFontIndirectW(&logfont);
    EXPECT_NE(font, nullptr);
    EXPECT_EQ(SelectObject(hdc, font), nullptr);
    DWORD size = GetFontData(hdc, kTestFontTableTag, 0, nullptr, 0);
    DWORD data_size = static_cast<DWORD>(strlen(kTestFontData));
    EXPECT_EQ(size, data_size);
    EXPECT_TRUE(DeleteObject(font));
    EXPECT_TRUE(DeleteDC(hdc));
}

TEST(GDIFontEmulationTest, GetFontDataInvalidTagSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyW);
    HFONT font = CreateFontIndirectW(&logfont);
    EXPECT_NE(font, nullptr);
    EXPECT_EQ(SelectObject(hdc, font), nullptr);
    DWORD size = GetFontData(hdc, kTestFontTableTag + 1, 0, nullptr, 0);
    EXPECT_EQ(size, GDI_ERROR);
    EXPECT_TRUE(DeleteObject(font));
    EXPECT_TRUE(DeleteDC(hdc));
}

TEST(GDIFontEmulationTest, GetFontDataInvalidFontSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    DWORD size = GetFontData(hdc, kTestFontTableTag, 0, nullptr, 0);
    EXPECT_EQ(size, GDI_ERROR);
    EXPECT_TRUE(DeleteDC(hdc));
}

TEST(GDIFontEmulationTest, GetFontDataDataSuccess)
{
    if (base::win::GetVersion() < base::win::VERSION_WIN8)
        return;
    TestSkFontMgr fontmgr;
    std::unique_ptr<GdiFontPatchData> patch_data(SetupTest());
    EXPECT_NE(patch_data, nullptr);
    HDC hdc = CreateCompatibleDC(0);
    EXPECT_NE(hdc, nullptr);
    LOGFONTW logfont = { 0 };
    InitLogFont(&logfont, kTestFontFamilyW);
    HFONT font = CreateFontIndirectW(&logfont);
    EXPECT_NE(font, nullptr);
    EXPECT_EQ(SelectObject(hdc, font), nullptr);
    DWORD data_size = static_cast<DWORD>(strlen(kTestFontData));
    std::vector<char> data(data_size);
    DWORD size = GetFontData(hdc, kTestFontTableTag, 0, &data[0], data.size());
    EXPECT_EQ(size, data_size);
    EXPECT_EQ(memcmp(&data[0], kTestFontData, data.size()), 0);
    EXPECT_TRUE(DeleteObject(font));
    EXPECT_TRUE(DeleteDC(hdc));
}

} // namespace content
